﻿using System;
using System.Collections.Generic;
using VisionarySystems.TimeRegi.Controller;
using VisionarySystems.TimeRegi.Database;
using VisionarySystems.TimeRegi.Model;
using VisionarySystems.TimeRegi.Model.TimePeriods;
using VisionarySystems.TimeRegi.Registry;
using VisionarySystems.TimeRegi.View;
using VisionarySystems.TimeRegi.View.WorkTimeCalculator;

namespace VisionarySystems.TimeRegi
{
	public enum TimeSlotTypes { WorkTime = 1, AutomaticBreak = 2, ManualBreak = 3 }

	/// <summary>
	/// Handles everything regarding time registration
	/// This is the buisness layer of the application
	/// </summary>
	public class TimeRegistrationHandler
	{
		#region private members
		private bool m_TimeSlotSaved;

		// Buisness Logic
		private AutomaticRegistrationController m_AutomaticRegistrationController;
		private IWorkTimeCalculator m_WorkTimeCalculator;
		private ITimePeriode m_TimePeriodController;
		private WorkTimeOverviewGenerator m_WorkTimeOverviewGenerator;
		private SummaryGenerator m_SummaryGenerator;

		private DataLayer m_DatabaseLayer;
		private bool m_Closing;
		private BreakTimeCalculator m_BreakTimeCalculator;
		private bool m_UpdateInProgress;
		private ICurrentTimeHandler m_CurrentTimeHandler;

		#endregion

		#region Ctor

		/// <summary>
		/// Class Constructor
		/// </summary>
		/// <param name="Model"></param>
		public TimeRegistrationHandler(TimePeriodModel Model)
		{
			// Init Classes
			Init(RegistryLayer.BreakPerDay, RegistryLayer.OverTimeStartDate, Model);
		}

		#endregion

		#region Properties
		//public TimeSpan WorkedToday
		//{
		//	get
		//	{
		//		try
		//		{
		//			return m_WorkTimeCalculator.GetTime(DateTime.Today.Date, DateTime.Today.Date, true);
		//		}
		//		catch (ArgumentOutOfRangeException)
		//		{
		//			Init(RegistryLayer.BreakPerDay);// Reinitalize
		//			return m_WorkTimeCalculator.GetTime(DateTime.Today.Date, DateTime.Today.Date, true);
		//		}
		//	}
		//}

		//public TimeSpan WorkedWeek
		//{
		//	get
		//	{
		//		try
		//		{
		//			return m_WorkTimeCalculator.GetTime(DateTime.Today.Date.AddDays(-6), DateTime.Today.Date);
		//		}
		//		catch (ArgumentOutOfRangeException)
		//		{
		//			Init( RegistryLayer.BreakPerDay);// Reinitalize
		//			return m_WorkTimeCalculator.GetTime(DateTime.Today.Date.AddDays(-6), DateTime.Today.Date);
		//		}
		//	}
		//}

		//public TimeSpan ExpectedWorkedWeek
		//{
		//	get
		//	{
		//		try
		//		{
		//			TimeSpan WorkedThisWeekNotToday = m_WorkTimeCalculator.GetTime(DateTime.Today.Date.AddDays(-6), DateTime.Today.Date.AddDays(-1), true);
		//			return WorkedThisWeekNotToday.Add(DaysOffCalculator.ExpectedWorkHoursPerDay(DateTime.Today.DayOfWeek));
		//		}
		//		catch (ArgumentOutOfRangeException)
		//		{
		//			m_WorkTimeCalculator.Clear();
		//			Init(RegistryLayer.BreakPerDay);// Reinitalize
		//			TimeSpan WorkedThisWeekNotToday = m_WorkTimeCalculator.GetTime(DateTime.Today.Date.AddDays(-6), DateTime.Today.Date.AddDays(-1), true);
		//			return WorkedThisWeekNotToday.Add(m_WorkTimeCalculator.ExpectedWorkHoursPerDay(DateTime.Today.DayOfWeek));
		//		}
		//	}
		//}

		public ITimePeriode TimePeriodController
		{
			get { return m_TimePeriodController; }
		}

		#endregion

		#region events

		public event Action TimeSlotsChanged;

		#endregion

		#region Public Methods

		public int AssembleTimeslots(TimePeriodeID TimePeriodeId)
		{
			int result;
			m_UpdateInProgress = true;
			try
			{
				ITimePeriode periode = m_TimePeriodController.GetTimePeriode(TimePeriodeId);
				List<int> removedIndexes;
				List<TimeSlot> newTimeSlots;
				if (periode != null && periode.AssembleTimeslots(out newTimeSlots, out removedIndexes))
				{
					result = removedIndexes.Count;
					foreach (int idx in removedIndexes)
						DataLayer.DeleteTimeSlot(idx);
					foreach (TimeSlot slot in newTimeSlots)
						SaveTimeSlot(slot);
				}
				else
					result = -1;
			}
			finally
			{
				m_UpdateInProgress = false;
			}
			NotifyObservers();
			return result;
		}

		public int DeleteTimePeriode(TimePeriodeID TimePeriodeID)
		{
			int result = -1;
			m_UpdateInProgress = true;
			try
			{
				ITimePeriode periode = m_TimePeriodController.GetTimePeriode(TimePeriodeID);
				List<int> removedIndexes = new List<int>();
				if (periode != null)
				{
					if (periode.DeleteTimeslots(ref removedIndexes))
					{
						result = removedIndexes.Count;
						foreach (int idx in removedIndexes)
							DataLayer.DeleteTimeSlot(idx);

						m_TimePeriodController.DeleteTimePeriode(periode);
					}
				}
			}
			finally
			{
				m_UpdateInProgress = false;
			}
			NotifyObservers();
			return result;
		}

		//public TimeSpan GetWorkedWeek(int StartDayNo, int StopDayNo)
		//{
		//	try
		//	{
		//		return m_WorkTimeCalculator.GetTime(DateTime.Today.Date.AddDays(-6), DateTime.Today.Date);

		//	}
		//	catch (ArgumentOutOfRangeException)
		//	{
		//		m_WorkTimeCalculator.Clear();
		//		Init(RegistryLayer.BreakPerDay);// Reinitalize
		//		return m_WorkTimeCalculator.GetTime(DateTime.Today.Date.AddDays(-6), DateTime.Today.Date);
		//	}
		//}

		/// <summary>
		/// Saves all information
		/// Should be called, when the system shots down
		/// </summary>
		public void ShutDown()
		{
			m_TimeSlotSaved = SaveCurrentTimeSlot();
		}

		public void ApplicationClosing()
		{
			m_Closing = true;
			m_TimeSlotSaved = SaveCurrentTimeSlot();
		}

		public void StartPause()
		{
			m_AutomaticRegistrationController.ProgramUseState = UseState.Unused; // Force the OnProgramUseStateChanged event to fire
		}

		public string GetOverview()
		{
			return m_WorkTimeOverviewGenerator.GenerateOverview();
		}

		public string GetSummary()
		{
			return m_SummaryGenerator.Generate();
		}

		#endregion

		#region Private Methods

		private void Init(TimeSpan BreakPerDayKey, DateTime OverTimeStartDate, TimePeriodModel Model)
		{
			m_CurrentTimeHandler = new CurrentTimeHandler(DateTime.Now);

			// Connect to database
			m_DatabaseLayer = new DataLayer();
			m_DatabaseLayer.CheckOrCreateDatabase();

			//Create AutomaticRegistrationController
			m_AutomaticRegistrationController = new AutomaticRegistrationController(new TimeSpan(0,4,0), m_CurrentTimeHandler);
			m_AutomaticRegistrationController.ProgramUseStateChanged += OnProgramUseStateChanged;

			//Create TimePeriodModel
			m_TimePeriodController = Model;// new TimePeriodModel();

			m_BreakTimeCalculator = new BreakTimeCalculator(BreakPerDayKey);
			// Init Calculator
			m_WorkTimeCalculator = new WorkTimeCalculatorFromTimeSlots(m_BreakTimeCalculator, OverTimeStartDate, m_CurrentTimeHandler);

			m_WorkTimeOverviewGenerator = new WorkTimeOverviewGenerator(m_WorkTimeCalculator);
			m_SummaryGenerator = new SummaryGenerator(m_WorkTimeCalculator, m_CurrentTimeHandler, m_BreakTimeCalculator);

			FillValuesFromDataLayer();
		}

		private void FillValuesFromDataLayer()
		{
			UpdateTimePeriodeController();
			m_WorkTimeCalculator.FillValues(m_TimePeriodController);
		}

		private void OnDataChanged()
		{
			if (!m_Closing)
			{
				UpdateTimePeriodeController();
				m_WorkTimeCalculator.FillValues(m_TimePeriodController);
				if (!m_UpdateInProgress)
					NotifyObservers();
			}
		}

		private void NotifyObservers()
		{
			OnTimeSlotsChanged();
		}

		private void OnTimeSlotsChanged()
		{
			if (TimeSlotsChanged != null)
				TimeSlotsChanged();
		}

		private void UpdateTimePeriodeController()
		{
			List<TimeSlot> timeSlotList = DataLayer.LoadAllTimeSlots();
			m_TimePeriodController.Add(timeSlotList);
			OnTimeSlotsChanged();
		}

		public bool SaveCurrentTimeSlot()
		{
			int newIdx;
			bool result = DataLayer.SaveTimeSlot(m_CurrentTimeHandler.StartTime, m_AutomaticRegistrationController.LastTimeUsed, TimeSlotTypes.WorkTime, m_TimeSlotSaved, out newIdx);
			return result;
		}

		public int SaveTimeSlot(TimeSlot NewTimeSlot)
		{
			int result;
			DataLayer.SaveTimeSlot(NewTimeSlot.Start, NewTimeSlot.Stop, TimeSlotTypes.WorkTime, false, out result);
			return result;
		}

		private void OnProgramUseStateChanged(UseState CurrentState, DateTime LastTimeUsed)
		{
			switch (CurrentState)
			{
				case UseState.InUse:
					m_CurrentTimeHandler.StartTime = DateTime.Now;
					m_TimeSlotSaved = false;
					//Reinitalize WorkTime Calculator
					//Init(RegistryLayer.BreakPerDay);// Reinitalize
					break;
				case UseState.Unused:
					m_TimeSlotSaved = SaveCurrentTimeSlot();
					m_CurrentTimeHandler.StartTime = DateTime.MaxValue;
					break;
				default:
					break;
			}
		}

		#endregion

		public void UpdateTimeSlot(TimeSlot Slot)
		{
			// Delete Slot
			DataLayer.DeleteTimeSlot(Slot.Idx);
			m_TimePeriodController.DeleteTimeSlot(Slot.Idx);

			// Add iut again
			SaveTimeSlot(Slot);
			m_TimePeriodController.Add(new List<TimeSlot>(Slot));
		}

		public ITimePeriode GetTimePeriode(TimePeriodeID ID)
		{
			return m_TimePeriodController.GetTimePeriode(ID);
		}
	}

	
}
